import { deepclone, setMapValueByJSON } from '@/utils'
import { CommandManage } from './command/CommandManage'
import { AddWidgetCommand } from './command/AddWidgetCommand'
import { AddContainerWidgetCommand } from './command/AddContainerWidgetCommand'
import { MoveWidgetCommand } from './command/MoveWidgetCommand'
import { MoveWidgetFromAToBContainerCommand } from './command/MoveWidgetFromAToBContainerCommand'
import { RemoveWidgetCommand } from './command/RemoveWidgetCommand'
import { ClaerWidgetCommand } from './command/ClearWidgetCommand'
import { ImportWidgetCommand } from './command/ImportWidgetCommand'
import { AddMultipleWidgetCommand } from './command/AddMultipleWidgetCommand'
import { AddColToGridCommand } from './command/AddColToGridCommand'
import { CopyWidgetToContainerCommand } from './command/CopyWidgetToContainerCommand'
import { Widget } from './widget'

import { ElMessage } from 'element-plus'
import { FormConfig } from './formConfig'
import { EventPropChangeCommand } from './command/EventPropChangeCommand'
export class Designer {
  constructor(option) {
    this.initDesigner(option)
  }
  // 返回表单数据
  static GETJSON(widget, data) {
    const children = widget.cols ?? widget.widgetList
    if (!children) {
      data[widget.options.propName] = widget.options.value
      return
    }
    for (let i of children) {
      Designer.GETJSON(i, data)
    }
    return JSON.stringify(data, null, ' ')
  }
  initDesigner(option) {
    this.widgetMap = setMapValueByJSON(new Map(), option.widgetList) // key:propName val: widget 用于快速查询物料
    this.command = new CommandManage(this) // 命令管理实例
    this.widgetList = option.widgetList // 物料集合
    this.cloneWidget = null // 拖拽时生成的临时物料
    this.multipleWidget = new Set() // 多选拖拽时生成的临时物料集合
    this.selectedWidget = null // 选中的物料
    this.parentContainer = this // 当前选中物料的父级容器，用于不同组物料移动时记录位置以便于回退
    this.formConfig = new FormConfig() // 表单配置
    this.selectedWidgetOption = null // 选中物料的属性配置
  }

  checkFieldMove() {}
  // 生成一个临时物料，主要是拖拽时不一定落到画布上，所以分两步：先生成临时物料，落到画布上时在添加真实物料
  handleWidgetClone(widget) {
    this.cloneWidget = new Widget(deepclone(widget), this.formConfig)
  }

  // 向容器中添加物料（默认容器是表单本身）
  addNewWidgetToContainer(container = this) {
    // 如果添加的是容器物料
    if (this.cloneWidget.category === 'container') {
      this.command.execute(
        new AddContainerWidgetCommand(
          this.cloneWidget,
          container.widgetList,
          this.widgetMap,
        ),
      )
    } // 单选
    else if (this.multipleWidget.size <= 1) {
      this.command.execute(
        new AddWidgetCommand(
          this.cloneWidget,
          container.widgetList,
          this.widgetMap,
        ),
      )
    } else {
      // 多选生成
      const multipleWidget = [...this.multipleWidget].map((item) => {
        const widget = new Widget(deepclone(item), this.formConfig)
        return widget
      })
      this.command.execute(
        new AddMultipleWidgetCommand(
          multipleWidget,
          container.widgetList,
          this.widgetMap,
        ),
      )
    }
    // 获取它在父容器中的位置
    const widgetList = container.widgetList ?? container.cols
    this.selectWidget(this.cloneWidget, widgetList.length - 1)
    // 清空克隆节点，用于判断向容器中添加物料时，若存在克隆节点，就是新增物料的情况
    this.cloneWidget = null
    this.multipleWidget.clear()
  }
  // 从一个容器中移动物料去另一个容器
  moveWidgetFromAToBContainer(e, BContainer) {
    const { oldIndex, newIndex } = e
    // 当前选中物料的父容器就是AContainer
    this.command.execute(
      new MoveWidgetFromAToBContainerCommand(
        oldIndex,
        newIndex,
        this.parentContainer,
        BContainer,
      ),
      false,
    )
  }
  // 拖拽更新位置
  dragUpdate(e) {
    const { oldIndex, newIndex } = e
    this.command.execute(
      new MoveWidgetCommand(this.widgetList, oldIndex, newIndex),
      false,
    )
  }
  // 双击时生成物料
  addFieldByDbClick(widget) {
    this.handleWidgetClone(widget)
    if (this.selectedWidget?.widgetList) {
      this.addNewWidgetToContainer(this.selectedWidget)
    } else {
      this.addNewWidgetToContainer()
    }
  }
  // 向栅格中增加列
  insetCol(grid) {
    this.command.execute(new AddColToGridCommand(grid.cols, this.widgetMap))
  }
  // 导入JSON
  addWidgetListByJSON(json) {
    this.command.execute(
      new ImportWidgetCommand(
        json,
        { widgetList: this.widgetList, formConfig: this.formConfig },
        this.widgetMap,
      ),
    )
    this.selectWidgetByContainerIndex(this, 0)
  }
  // 从一个容器中移除当前选中物料
  removeWidgetFromContainer(widgetList = this.widgetList) {
    this.command.execute(
      new RemoveWidgetCommand(
        this.selectedWidget,
        widgetList,
        this.selectWidget.bind(this),
        this.widgetMap,
      ),
    )
  }
  // 移除所有物料
  clearWidget() {
    this.command.execute(
      new ClaerWidgetCommand(
        this.widgetList,
        this.selectWidget.bind(this),
        this.selectedWidget,
        this.widgetMap,
      ),
    )
  }
  // 返回选中物料属性
  getSeletedWidgetOptions() {
    return this.selectedWidget?.options ?? {}
  }
  // 选择物料
  selectWidgetByWidgetListIndex(widgetList, index) {
    const target = widgetList[index]
    if (!target) {
      ElMessage(`已经移动到最${index < 0 ? '上面' : '下面'}`)
      return
    }
    this.selectedWidget = target
    this.selectedWidget.index = index
    window.dispatchEvent(new CustomEvent('select_widget'))
  }
  // 当且仅当通过鼠标点击选择物料时，会得到draggable传来的index，表明它在父容器中的位置，用这个属性来控制选择兄弟物料
  selectWidget(widget, index) {
    this.selectedWidget = widget
    index !== null && index !== undefined && (this.selectedWidget.index = index)
    window.dispatchEvent(new CustomEvent('select_widget'))
  }
  copyWidgetToContainer(widget, widgetList) {
    this.command.execute(
      new CopyWidgetToContainerCommand(widget, widgetList, this.widgetMap),
    )
  }
  // 检测是否存在重复的组件名称
  checkPropName(propName) {
    return this.widgetMap.has(propName)
  }
  // 获取表单数据
  getJSON() {
    const data = {}
    for (let [propName, widget] of this.widgetMap.entries()) {
      data[propName] = widget.options.value
    }
    return JSON.stringify(data, null, '  ')
  }
  resetData() {
    this.widgetList.forEach((item) => item.resetValue())
  }
  // 通过属性名修改值
  setValByPropName(propName, val) {
    const targetWidget = this.widgetMap.get(propName)
    if (targetWidget) {
      targetWidget.options.value = val
    }
  }
  getWidgetByPropName(propName) {
    return this.widgetMap.get(propName)
  }
  eventChange() {
    this.command.execute(new EventPropChangeCommand(), false, false)
  }
}
